//
// UART
// Protocôle : on envoie des chaines de caractères se terminant par le
// caractère nulle. Ainsi :
//	- À l'envoie : chaine + \0
//	- Reception : jusqu'à se qu'on lise le \0
//


#include "gpio.h"
#include "asm_tools.h"


//
// Registres UART
#define UART_DR		0x20201000u	// Data register
#define UART_RSRECR	0x20201004u
#define UART_FR		0x20201018u	// Flag register
#define UART_IBRD	0x20201024u	// Integer Daud rate divisor
#define UART_FBRD	0x20201028u	// Fractional Baud rate divisor
#define UART_LCRH	0x2020102Cu	// Line control register
#define UART_CR 	0x20201030u	// Control register
#define UART_IFLS	0x20201034u	// Interupt FIFO level select register
#define UART_IMSC	0x20201038u	// Interupt mask set clear register
#define UART_RIS	0x2020103Cu	// Raw Interupt Status register
#define UART_MIS	0x20201040u	// Masked Interupt Status Register
#define UART_ICR	0x20201044u	// Interupt clear register
#define UART_ITCR	0x20201080u	// Test Control register
#define UART_ITIP	0x20201084u	// Integration test input reg
#define UART_ITOP	0x20201088u	// Integration test output reg
#define UART_TDR	0x2020108Cu	// Test data reg
// Constantes GPIO utiles aux UART
#define UART_TXD0_PIN	14u	// UART TDX0 pin sur ALT0
#define UART_RDX0_PIN	15u // UART RDX0 pin sur ALT0
#define GPIO_ALT0		4u	// GPIO fonction ALT0
#define GPIO_TDX0_OFF	((14u % 10u) * 3u)
#define GPIO_RDX0_OFF	((15u % 10u) * 3u)

//
// Constante des puissances de 10
static const int TEN_POW[10] = {	1,
									10,
									100,
									1000,
									10000,
									100000,
							        1000000,
							        10000000,
							        100000000,
							        1000000000 };
// Variable publique d'erreur
int uart_error;

//
// Initialisation
void uart_init(void)
{
	// On désactive UART
	set32(UART_CR, 0u);

	// Initialisation GPIO (14 et 15)
	// On règle la fonction sur ALT0 pour les bons pins
	set32(GPIO_FSEL1, get32(GPIO_FSEL1) | (GPIO_ALT0 << GPIO_TDX0_OFF));
	set32(GPIO_FSEL1, get32(GPIO_FSEL1) | (GPIO_ALT0 << GPIO_RDX0_OFF));
	// On règle le pull down
	set32(GPIO_PUD, 0u);
	delay(150u);
	set32(GPIO_PUDCLK0, (1u << UART_TXD0_PIN) | (1u << UART_RDX0_PIN));
	delay(150u);
	set32(GPIO_PUD, 0u);
	set32(GPIO_PUDCLK0, 0u);

	// On initialisation UART
	// On clear la line
	set32(UART_LCRH, 0u);
	// On clear les intéruptions
	set32(UART_ICR, 0u);
	// On règles le baud rate à 115200 baud
	set32(UART_IBRD, 1u);
	set32(UART_FBRD, 40u);
	// Line control :
	// - break disable
	// - parity disable
	// - stop bits disable
	// - fifo enable
	// - word length = 8 bits
	set32(UART_LCRH, (1u << 4u) | (3u << 5u));
	// Seuil FIFO
	// - On utilise pas la transmit fifo
	// - receive fifo à 1/2
	set32(UART_IFLS, 0u);
	// Interruption
	// - On active les interruptions de la receive FIFO et les erreurs
	set32(UART_IMSC, (1u << 1u) | (1u << 4u) | (1u << 5u)
						| (1u << 6u) | (1u << 7u) | (1u << 8u)
						| (1u << 9u) | (1u << 10u));
	// Controle :
	// - on active l'UART
	// - pas de loopback
	// - activation de la réception/émission
	// - pas de RTS
	// - pas de flowcontrol
	set32(UART_CR, (1u << 0u) | (1u << 8u) | (1u << 9u));
}

//
// Permet d'envoyer des chaine de caractère. Celle-ci doit se terminer
//	par le caractère nul.
void uart_send_str(const char *data)
{
	if (*data == 0)
	{
		return;
	}

	do
	{
		// On attend que l'UART soit disponible
		while ((get32(UART_FR) & (1u << 5u)) != 0u);
		// On écrit la données
		set32(UART_DR, (unsigned int)*(data++));

	} while (*data != 0);

	// On envoie la caractère nul
	// On attend que l'UART soit disponible
	while ((get32(UART_FR) & (1u << 5u)) != 0u);
	// On écrit la données
	set32(UART_DR, 0u);
}

//
// Permet d'envoyer un entier signé
void uart_send_int(int n)
{
	int p;
	int i;
	int j = 0;
	char str[16];

	if (n == 0)
	{
		str[0] = '0';
		str[1] = 0;
		uart_send_str(str);
		return;
	}

	if ((n & 0x80000000) != 0)
	{
		str[j++] = '-';
		n -= 1;
		n = ~n;
	}

	for (p = 9; p >= 0; p--)
	{
		i = 0;
		while (n >= TEN_POW[p])
		{
			n -= TEN_POW[p];
			i++;
		}
		if (i > 0)
		{
			str[j++] = (char)(i + 48);
		}
	}

	str[j] = 0;
	uart_send_str(str);
}

//
// Permet de recevoir des caractères
// Block jusqu'a la reception de n-1 caractères, ou la reception
//	du caractère nul.
// NOTE : Un caractère nul est placé automatiquement en fin de
//	chaine.
int uart_receive_str(char *buffer, unsigned int n)
{
	unsigned int byte;
	int i;

	if (n == 0u)
	{
		return 0;
	}

	i = 0;
	n--;
	do
	{
		// On attend que se ne soit pas vide
		while ((get32(UART_FR) & (1u << 4u)) != 0);
		// Lecture du byte
		byte = get32(UART_DR) & 0xFFu;

		// On vérifique que se n'est pas la fin
		if (byte == 0u)
		{
			break;
		}

		*(buffer++) = (char)byte;
		i++;
	}
	while (--n != 0u);

	*buffer = 0; // Caractère de fin

	return i;
}

//
// Permet de lire un entier signé.
// Renvoie la valeur lue
// NOTE : en cas d'erreur, uart_error est mis à -1
int uart_receive_int(void)
{
	char buffer[16];
	char *data = buffer;
	int neg;
	int i, j, n;

	i = uart_receive_str(buffer, 16u);

	if (*data == '-')
	{
		neg = 1;
		data++;
		i--;
	}
	else
	{
		neg = 0;
	}

	if ((i <= 0) || (i > 9))
	{
		uart_error = -1;
	}

	n = 0;
	do
	{
		j = (int)*(data++);
		if ((j < 48) || (j > 57))
		{
			uart_error = -1;
		}
		n += (j - 48) * TEN_POW[--i];
	}
	while (i != 0);

	if (neg == 1)
	{
		n = -(n);
	}

	return n;
}

//
// Permet de savoir si la receive fifo est vide
int uart_is_receive_fifo_empty(void)
{
	if ((get32(UART_FR) & (1 << 4)) != 0)
	{
		return 0;
	}

	return 1;
}
